<?php
    
	/**
	* api.php
	* Este es el script  que actúa como receptor (server) de la API.
	* Se analizan y procesan las peticiones de los clientes para posteriormente dar 
	* respuesta a los mismos.
	* @author Sebastian Lara <slara@grvertice.com>
	* @version 1.0
	* @created 19/DEC/2012
	* @copyright
	*/
	
	require ('library/rest.class.php');
	require ('library/xmlwriter.class.php');
	require ('library/language.class.php');
	require ('library/rijndaelhex.php');
	require ('model/server.class.php');


	class API extends REST {

		const DB_SERVER          = 'localhost';
		const DB_USER     		 = 'root';
		const DB_PASSWORD 		 = '';
		const DB          		 = 'movitech_fat';

		const NO_DATA_FOUND 	 = 'NO_DATA_FOUND';
        const INTERNAL_ERROR     = 'INTERNAL_ERROR';
        const NOT_SUPPORTED  	 = 'NOT_SUPPORTED';

		const TAG_RESPONSE_API   = 'API'; // etiqueta que cubre toda la respuesta XML
		const TAG_ERROR_RESPONSE = 'ERROR_MESSAGE'; // etiqueta para los errores
		 
		private $db;
		private $lang; // lenguaje en que responde  la API
		private $xmlWriter; // respuesta a las peticiones de los clientes
		private $rijndaelHex; // librería de encriptación
		private $server; // modelo para responder a los comandos de la aplicación
		private $key; //llave de seguridad otorgada por el cliente
		private $command; // commando a ejecutar en la API
		private $parametersRequiredList;
		private $dataUnlockService; // datos del servicio solicitado para el unlock.

		/**
		* Función constructora de la clase, inicializa las variables locales 
		* para trabajar.
		* @param none
		* @return none
		* @created 19/DEC/2012
		* @author Sebastián Lara <slara@grverice.com>
		*/
		public function API() {
			parent::__construct(); // Inicializa el contructor padre
			set_time_limit(1200);
			$this->db           = NULL;
			$this->lang         = new Language();
			$this->xmlWriter    = new Xml_Writer();
			$this->rijndaelHex  = new RijndaelHex();
			$this->server 		= new Server();

			$this->key         			  = NULL;
			$this->command     		      = NULL;
			$this->parametersRequiredList = NULL;
			$this->dataUnlockService	  = NULL;

			$this->dbConnect();
		}

		
		/**
		* Función responsable de establecer la conexión con la base de datos
		* @param none
		* @return none
		* @created 19/DEC/2012
		* @author Sebastián Lara <slara@grverice.com>
		*/
		private function dbConnect() {
			$this->db = mysql_connect (self::DB_SERVER, self::DB_USER, self::DB_PASSWORD);
			
			if ($this->db) {
				mysql_select_db(self::DB, $this->db);				
			} else {
				$this->sendResponse(325, NULL, 500);
			}
		}

		
		/**
		 * Función pública para acceder a la API, en ésta se analiza, se valida y se
		 * la procesa la petición que es enviada por el cliente para dar una respuesta
		 * al mismo
		 * @param none
		 * @return none
		 * @created 20/DEC/2012
		 * @author Sebastián Lara <slara@grvertice.com>
		 */
		public function processApi() {
			$this->key = $this->searchRequest('KEY');
			$this->command = $this->searchRequest('COMMAND');
			$this->validateApiKey();
			$this->examineCommand();
		}


		/**
		* Función responsable de validar el API KEY que se recibe corresponde
		* a la de un cliente real.
		* @param none
		* @return bollean, resultado de la validación
		* @created 11/JAN/2013
		* @author Sebastián Lara <slara@grvertice.com>
		*/
		private function validateApiKey() {
			if (empty ($this->key)) {
				$this->sendResponse(326, NULL, 500);
			} else {
				$keyDecrypted = str_replace('-', '', $this->key);
				$keyDecrypted = $this->rijndaelHex->lindecrypthex($keyDecrypted);
                // al desencriptar el API_KEY, quedacon el formato [NUM]-[NUM]
                // el primer NUM se refiere al ID del cliente, el segundo a 
                // cuantas veces ha cambiado el API_KEY
				$keyDecrypted = explode('-', $keyDecrypted);

				$apiKeyMU = $this->rijndaelHex->linencrypthex($this->key);
				$cliCode = $keyDecrypted[0];
				$secretNumber = $keyDecrypted[1];

				$validationApiKey = $this->server->validateApiKey($apiKeyMU, $cliCode, $secretNumber);

				if ($validationApiKey === (self :: INTERNAL_ERROR)) {
					$this->sendResponse(325, NULL, 500);
				} elseif ($validationApiKey === FALSE) {
					$this->sendResponse(326);
				}
			}
		}
		

		/**
		* Función responsable de examinar el comando que se recibe desde el
		* cliente y responder según sea el caso.
		* @param none
		* @return none
		* @created 15/JAN/2013
		* @author Sebastián Lara
		*/
		private function examineCommand() {
			$data = NULL;

			$this->validateParametersCommand();

			switch ($this->command) {
				case 'ACCOUNT_INFO':
					$data = $this->server->getAccountInfo();

					// el email está encriptado en la bd, se desencripta:
					if (isset ($data[0]['EMAIL'])) {
						$data[0]['EMAIL'] = $this->rijndaelHex->lindecrypthex($data[0]['EMAIL']);
					}

					break;
				case 'GET_MOBILES':
					$brandId = $this->parametersRequiredList['BRAND_ID']['VALUE'];

					$data = $this->server->getMobiles($brandId);
					break;
				case 'GET_NETWORKS':
					$countryId = $this->parametersRequiredList['COUNTRY_ID']['VALUE'];

					$data = $this->server->getNetworks($countryId);
					break;
				case 'GET_SERVICES':
					$groupId = $this->parametersRequiredList['GROUP_ID']['VALUE'];

					$data = $this->server->getServices($groupId);
					break;
				case 'GET_SERVICE_MOBILES':
					$serviceId = $this->parametersRequiredList['SERVICE_ID']['VALUE'];

					$data = $this->server->getServiceMobiles($serviceId);
					break;

				case 'GET_ORDERS':
					$orderStatus  = $this->parametersRequiredList['STATUS']['VALUE'];
					$serviceId    = $this->parametersRequiredList['SERVICE_ID']['VALUE'];
					$imei 		  = $this->parametersRequiredList['IMEI']['VALUE'];
					$orderId 	  = $this->parametersRequiredList['ID']['VALUE'];
					$dateFrom 	  = $this->parametersRequiredList['DATE_FROM']['VALUE'];
					$dateTo 	  = $this->parametersRequiredList['DATE_TO']['VALUE'];
					$deliveryFrom = $this->parametersRequiredList['DELIVERY_FROM']['VALUE'];
					$deliveryTo   = $this->parametersRequiredList['DELIVERY_TO']['VALUE'];

					$data = $this->server->getOrders($orderStatus, $serviceId,
						$imei, $orderId, $dateFrom, $dateTo, $deliveryFrom, $deliveryTo);
					break;

				case 'SEND_ORDER':
					// se validan que los parámetros requeridos por master-unlock
					// tengan el formato correcto
					$this->validateRequestOrderParameters();
					// se valida que los créditos del cliente sean suficientes
					// para enviar la liberación
					$this->validateCredits();
					// se valida que los parámetros de entrada sean correctos
					// teniendo en cuenta la forma de liberar.
					$this->validateParametersUnlockWay();

					$this->sendResponse('OK');
					break;

				default: //  comando no encontrado
					$this->sendResponse(327, array($this->command));
					break;
			}

			if (is_array($data) && !empty($data)) {
				$this->proccessData($data);
			} elseif ($data === (self :: NO_DATA_FOUND)) {
				$this->sendResponse(328);
			} else {
				$this->sendResponse(325, NULL, 500);
			}
		}


		/**
		* Función responsable de validar que los parámetros que son 
		* requeridos por un comando.
		* @param none
		* @return none
		* @created 18/JAN/2013
		* @author Sebastián Lara <slara@grvertice.com>
		*/
		private function validateParametersCommand() {

			switch ($this->command) {
				case 'ACCOUNT_INFO':
					// comando sin porámetros.
					break;

				case 'GET_MOBILES':
					$brandParameterKey = 'BRAND_ID';
					$brandId = $this->searchRequest($brandParameterKey);

					if ($brandId !== NULL) {
						$isValidBrand = $this->server->validateBrand($brandId);

						if (!is_numeric($brandId) || (int) $brandId < 1 ) {
							$this->sendResponse(331, array ($brandParameterKey, $brandId));
						}

						if ($isValidBrand === self :: INTERNAL_ERROR) {
							$this->sendResponse(325);
						} elseif ($isValidBrand === self :: NO_DATA_FOUND) {
							$this->sendResponse(328);
						} elseif ($isValidBrand === FALSE) {
							// marca no encontrada
							$this->sendResponse(330, array ($brandParameterKey, $brandId));
						}
					}

					$this->parametersRequiredList = array (
						$brandParameterKey => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $brandId,
						),

					);
					break;

				case 'GET_NETWORKS':
					$countryIdParameterKey = 'COUNTRY_ID';
					$countryId = $this->searchRequest($countryIdParameterKey);

					if ($countryId !== NULL) {
						$isValidCountry = $this->server->validateCountry($countryId);

						if (!is_numeric($countryId) || (int) $countryId < 1 ) {
							$this->sendResponse(331, array ($countryIdParameterKey, $countryId));
						}

						if ($isValidCountry === self :: INTERNAL_ERROR) {
							$this->sendResponse(325);
						} elseif ($isValidCountry === self :: NO_DATA_FOUND) {
							$this->sendResponse(328);
						} elseif ($isValidCountry === FALSE) {
							// red no encontrada
							$this->sendResponse(330, array ($countryIdParameterKey, $countryId));
						}
					}
					
					$this->parametersRequiredList = array (
						$countryIdParameterKey => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $countryId,
						),

					);
					break;

				case 'GET_SERVICES':
					$groupParameterKey = 'GROUP_ID';
					$groupId = $this->searchRequest($groupParameterKey);

					if ($groupId !== NULL) {
						$isValidGroup = $this->server->validateGroup($groupId);

						if (!is_numeric($groupId) || (int) $groupId < 1) {
							// el valor debe ser numérico positivo
							$this->sendResponse(331, array ($groupParameterKey, $groupId));
						}

						if ($isValidGroup === self :: INTERNAL_ERROR) {
							$this->sendResponse(325);
						} elseif ($isValidGroup === self :: NO_DATA_FOUND) {
							$this->sendResponse(328);
						} elseif ($isValidGroup === FALSE) {
							// grupo no encontrado
							$this->sendResponse(330, array ($groupParameterKey, $groupId));
						}
					}

					$this->parametersRequiredList = array (
						$groupParameterKey => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $groupId,
						),

					);
					break;

				case 'GET_SERVICE_MOBILES':
					$serviceParameterKey = 'SERVICE_ID';
					$serviceId = $this->searchRequest($serviceParameterKey);

					if ($serviceId !== NULL) {
						$isValidService = $this->server->validateService($serviceId);

						if (!is_numeric($serviceId) || (int) $serviceId < 1) {
							// el valor debe ser numérico positivo
							$this->sendResponse(331, array ($serviceParameterKey, $serviceId));
						}

						if ($isValidService === self :: INTERNAL_ERROR) {
							$this->sendResponse(325);
						} elseif ($isValidService === self :: NO_DATA_FOUND) {
							$this->sendResponse(328);
						} elseif ($isValidService === FALSE) {
							// servicio no encontrado
							$this->sendResponse(330, array ($serviceParameterKey, $serviceId));
						}
					}

					$this->parametersRequiredList = array (
						'SERVICE_ID' => array (
							'REQUIRED' => TRUE,
							'VALUE'    => $serviceId,
						),

					);
					break;

				case 'GET_ORDERS':
					$statusParameterKey   	  = 'STATUS';
					$serviceParameterKey  	  = 'SERVICE_ID';
					$imeiParameterKey     	  = 'IMEI';
					$orderIdParameterKey  	  = 'ID';
					$dateFromParameterKey 	  = 'DATE_FROM';
					$dateToParameterKey   	  = 'DATE_TO';
					$deliveryFromParameterKey = 'DELIVERY_FROM';
					$deliveryToParameterKey   = 'DELIVERY_TO';

					
					$orderStatus  = $this->searchRequest($statusParameterKey);
					$serviceId    = $this->searchRequest($serviceParameterKey);
					$imei   	  = $this->searchRequest($imeiParameterKey);
					$orderId   	  = $this->searchRequest($orderIdParameterKey);
					$dateFrom 	  = $this->searchRequest($dateFromParameterKey);
					$dateTo   	  = $this->searchRequest($dateToParameterKey);
					$deliveryFrom = $this->searchRequest($deliveryFromParameterKey);
					$deliveryTo   = $this->searchRequest($deliveryToParameterKey);


					if ($orderStatus !== NULL) {
						$originalOrderStatus = $orderStatus;
		                $orderStatus = $this->server->getCorrespondingValue('VALUES_UNLOCK_REVERSE', $orderStatus);
		                
		                if ($orderStatus === NULL) {
		                    $this->sendResponse(332, array ($statusParameterKey, 
		                    		'WAITING | DELIVERED | VERIFICATION | NOT_IN_DATABASE',
		                    		$originalOrderStatus));
		                }
					}

					if ($serviceId !== NULL) {
						$isValidService = $this->server->validateService($serviceId);

						if (!is_numeric($serviceId) || (int) $serviceId < 1) {
							// el valor debe ser numérico positivo
							$this->sendResponse(331, array ($serviceParameterKey, $serviceId));
						}

						if ($isValidService === self :: INTERNAL_ERROR) {
							$this->sendResponse(325);
						} elseif ($isValidService === self :: NO_DATA_FOUND) {
							$this->sendResponse(328);
						} elseif ($isValidService === FALSE) {
							// servicio no encontrado
							$this->sendResponse(330, array ($serviceParameterKey, $serviceId));
						}
					}

					if ($imei !== NULL) {
						/**
						 * En este caso se valida que el IMEI sea numérico y 
						 * tenga 15 dígitos, FALSE es para que el IMEI no lo 
						 * evalúe contra el algorito de validación estándar. 
						 */
						$isValidImei = $this->validateIMEI($imei, FALSE);

						if (!is_numeric($imei) || (int) $imei < 1) {
							// el valor debe ser numérico positivo
							$this->sendResponse(331, array ($imeiParameterKey, $imei));
						} elseif ($isValidImei === FALSE) {
							// imei inválido
							$this->sendResponse(333, array ('IMEI', '15', 'IMEI', $imei));
						}
					}

					if ($orderId !== NULL) {
						$isValidOrderId = $this->server->validateOrderId($orderId);

						if (!is_numeric($orderId) || (int) $orderId < 1) {
							// el valor debe ser numérico positivo
							$this->sendResponse(331, array ($orderIdParameterKey, $orderId));
						}

						if ($isValidOrderId === self :: INTERNAL_ERROR) {
							$this->sendResponse(325);
						} elseif ($isValidOrderId === self :: NO_DATA_FOUND) {
							$this->sendResponse(328);
						} elseif ($isValidOrderId === FALSE) {
							// liberación no encontrada
							$this->sendResponse(330, array ($orderIdParameterKey, $orderId));
						}
					}

					if ($dateFrom !== NULL) {
						$isValidDate = $this->validateDate($dateFrom);

						if ($isValidDate === FALSE) {
							// formato de fecha inválido
							$this->sendResponse(334, array ($dateFromParameterKey, 'YYYY-MM-DD', $dateFrom));
						}
					}

					if ($dateTo !== NULL) {
						$isValidDate = $this->validateDate($dateTo);

						if ($isValidDate === FALSE) {
							// formato de fecha inválido
							$this->sendResponse(334, array ($dateToParameterKey, 'YYYY-MM-DD', $dateTo));
						}
					}

					if ($deliveryFrom !== NULL) {
						$isValidDate = $this->validateDate($deliveryFrom);

						if ($isValidDate === FALSE) {
							// formato de fecha inválido
							$this->sendResponse(334, array ($deliveryFromParameterKey, 'YYYY-MM-DD', $deliveryFrom));
						}
					}

					if ($deliveryTo !== NULL) {
						$isValidDate = $this->validateDate($deliveryTo);

						if ($isValidDate === FALSE) {
							// formato de fecha inválido
							$this->sendResponse(334, array ($deliveryToParameterKey, 'YYYY-MM-DD', $deliveryTo));
						}
					}

					$this->parametersRequiredList = array (
						'STATUS'     	=> array (
							'REQUIRED' => FALSE,
							'VALUE'    => $orderStatus,
						),
						'SERVICE_ID' 	=> array (
							'REQUIRED' => FALSE,
							'VALUE'    => $serviceId,
						),
						'IMEI'       	=> array (
							'REQUIRED' => FALSE,
							'VALUE'    => $imei,
						),
						'ID'            => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $orderId,
						),
						'DATE_FROM'     => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $dateFrom,
						),
						'DATE_TO'  		=> array (
							'REQUIRED' => FALSE,
							'VALUE'    => $dateTo,
						),
						'DELIVERY_FROM' => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $deliveryFrom,
						),
						'DELIVERY_TO'   => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $deliveryTo,
						),

					);
					break;

				case 'SEND_ORDER':
					$serviceIdParameterKey 	 = 'SERVICE_ID';
					$imeiParameterKey 	 	 = 'IMEI';
					$countryIdParameterKey 	 = 'COUNTRY_ID';
					$mobileIdParameterKey 	 = 'MOBILE_ID';
					$networkIdParameterKey 	 = 'NETWORK_ID';
					$mepParameterKey 		 = 'MEP';
					$providerIdParameterKey  = 'PROVIDER_ID';
					$userParameterKey 		 = 'USER';
					$serieParameterKey 		 = 'SERIE';
					$idBoxParameterKey 		 = 'ID_BOX';
					$descriptionParameterKey = 'DESCRIPTION';
					$nickParameterKey 		 = 'NICK';
					$passwordParameterKey 	 = 'PASSWORD';
					$rmParameterKey 		 = 'RM';


					$serviceId 	 = $this->searchRequest($serviceIdParameterKey);
					$imei 		 = $this->searchRequest($imeiParameterKey);
					$countryId 	 = $this->searchRequest($countryIdParameterKey);
					$mobileId 	 = $this->searchRequest($mobileIdParameterKey);
					$networkId	 = $this->searchRequest($networkIdParameterKey);
					$mep 		 = $this->searchRequest($mepParameterKey);
					$providerId  = $this->searchRequest($providerIdParameterKey);
					$user 		 = $this->searchRequest($userParameterKey);
					$serie 		 = $this->searchRequest($serieParameterKey);
					$idBox 		 = $this->searchRequest($idBoxParameterKey);
					$description = $this->searchRequest($descriptionParameterKey);
					$nick 		 = $this->searchRequest($nickParameterKey);
					$password    = $this->searchRequest($passwordParameterKey);
					$rm 		 = $this->searchRequest($rmParameterKey);

					if ($serviceId !== NULL) {
						$isValidService = $this->server->validateService($serviceId);

						if (!is_numeric ($serviceId) || (int) $serviceId < 1) {
							$this->sendResponse(331, array ($serviceIdParameterKey, $serviceId));
						}

						if ($isValidService === self :: INTERNAL_ERROR) {
							$this->sendResponse(325);
						} elseif ($isValidService === self :: NO_DATA_FOUND) {
							$this->sendResponse(328);
						} elseif ($isValidService === FALSE) {
							// parámetro desconocido
							$this->sendResponse(330, array ($serviceIdParameterKey, $serviceId));
						}
					}

					$this->parametersRequiredList = array (
						'SERVICE_ID' => array (
							'REQUIRED' => TRUE,
							'VALUE'    => $serviceId,
						),
						'IMEI' => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $imei,
						),
						'COUNTRY_ID' => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $countryId,
						),
						'MOBILE_ID' => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $mobileId,
						),
						'NETWORK_ID' => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $networkId,
						),
						'MEP' => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $mep,
						),
						'PROVIDER_ID' => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $providerId,
						),
						'USER' => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $user,
						),
						'SERIE' => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $serie,
						),
						'ID_BOX' => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $idBox,
						),
						'DESCRIPTION' => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $description,
						),
						'NICK' => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $nick,
						),
						'PASSWORD' => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $password,
						),
						'RM' => array (
							'REQUIRED' => FALSE,
							'VALUE'    => $rm,
						),
					);

					

					break;

				default: //  comando no encontrado
					// #Code
					break;
			}

			if (!is_null ($this->parametersRequiredList)) {
				foreach ($this->parametersRequiredList as $key => $value) {
					if (($value['REQUIRED'] === TRUE) && empty ($value['VALUE'])) {
						$this->sendResponse(329, array ($key));
					} 
				}
			}

		}


		/**
		* Función responsable de responder con los posibles errores que se puedan
		* presentar en la API.
		* @param int $responseCode, código de respuesta para responder con un mensaje
		* asociado al mismo.
		* @param array $additionalInformationList, información específica para
		* mostrar en el mensaje de error.
		* @param $statusResponseHeader, estado con el que se responde en la
		* cabecera de la respuesta.
		* @return none
		* @created 14/JAN/2013
		* @author Sebastián Lara <slara@grvertice.com>
		*/
		private function sendResponse($responseCode, $additionalInformationList = array (), 
				$statusResponseHeader = 200) {
			$this->xmlWriter->openElementParent(self :: TAG_RESPONSE_API);
			$this->xmlWriter->addElementChild(self :: TAG_ERROR_RESPONSE, 
			$this->lang->getText($responseCode, $additionalInformationList));
			$this->xmlWriter->closeElementParent();
			$this->response($this->xmlWriter->getXml(), $statusResponseHeader);
		}


		/**
		* Función encargada de buscar el valor de una petición (si es que
		* existe), retorna NULL en caso de no encotrarla.
		* @param string $request, representa la petición que se quiere obtener
		* el valor.
		* @return string $requestValue, valor de la petición buscada
		* @created 21/DEC/2012
		* @author Sebastián Lara <slara@grvertice.com>
		*/
		private function searchRequest($request) {
			$requestValue = NULL;

			if (! empty ($this->requestList)) {
				$issetRequest = array_key_exists ($request, $this->requestList);

				if ($issetRequest === true) {
					$requestValue = $this->requestList[$request];
				}
			}

			return $requestValue;
		}


		/**
		* Función responsable de procesar un arreglo de datos y convertirlo en
		* formato XML.
		* @param array, datos a procesar.
		* @return XML, datos procesados en formato XML.
		* @created 02/JAN/2013
		* @author Sebastián Lara <slara@grverice.com>
		*/
		function proccessData($data) {
			$this->xmlWriter = new Xml_Writer();

			$countData = count ($data);
			$dataKeysList = array_keys($data);
			$i = 0;

			$this->xmlWriter->openElementParent('API');
			while ($i < $countData) {
				$element = $data[$dataKeysList[$i]];
				$keysList = array_keys($element);
				$counKeys = count($keysList);
				$j = 0;

				if (isset ($element['TAG']) && ! empty ($element['TAG'])) {
					$this->xmlWriter->openElementParent($element['TAG']);
				}

				while ($j < $counKeys) {
					$child = $element[$keysList[$j]];
					if (is_array($child)) {					
						$countChild = count ($child);
						$childKeysList = array_keys($child);
						$k = 0;
						while ($k < $countChild) {
							$elementChild = $child[$childKeysList[$k]];
							$elementChildKeysList = array_keys($elementChild);
							$counElementChildKeys = count($elementChildKeysList);

							$l = 0;
							$this->xmlWriter->openElementParent($elementChild['TAG']);

							while ($l < $counElementChildKeys) {
								$childAux = $elementChild[$elementChildKeysList[$l]];

								if ($elementChildKeysList[$l] != 'TAG') {
									$this->xmlWriter->addElementChild($elementChildKeysList[$l], $childAux);
								}
								$l++;
							}

							$this->xmlWriter->closeElementParent();
							$k++;
						}

					} elseif ($keysList[$j] != 'TAG') {
						$this->xmlWriter->addElementChild($keysList[$j], $child);
					}
					$j++;
				}
				
				if (isset ($element['TAG']) && ! empty ($element['TAG'])) {
					$this->xmlWriter->closeElementParent();
				}

				$i++;
			}
			$this->xmlWriter->closeElementParent();

			$this->response($this->xmlWriter->getXml());
		}

		
		/**
		* Función responsable de validar que un IMEI tenga el formato correcto.
		* @param string $IMEI, IMEI a validar
		* @param boolean $Checksum, bandera para saber si validar el IMEI con
		* el algoritmo estandar.
		* @created 31/JAN/2013
		* @return boolean, retorna TRUE en caso de que el IMEI sea válido, 
		* FALSE en caso contrario.
		*
		*/
		function validateIMEI( $IMEI, $Checksum = TRUE )
		{
			if (is_string($IMEI))
			{
				if (@ereg('^[0-9]{15}$', $IMEI))
				{
					if (! $Checksum)
					{
						return TRUE;
					}

					for ($i = 0, $Sum = 0; $i < 14; $i++)
					{
						$Tmp = $IMEI[$i] * ( ($i % 2) + 1 );
						$Sum += ($Tmp % 10) + intval($Tmp / 10);
					}
					
					return ( ( ( 10 - ( $Sum % 10 ) ) % 10 ) == $IMEI[14] );
				}
			}
			
			return FALSE;
		}


		/**
		* Función responsable de verificar que un MEP tenga el formato correcto	
		* @param string $value, cadena con el MEP a validar.
		* @return boolean, retorna TRUE si el número MEP es correcto, FALSE en 
		* caso contrario.
		* @created 21/FEB/2013
		* @author Sebastián Lara <slara@grvertice.com>
		*/
		function validateMEP($String) {
			return @ereg ('^MEP\\-[0-9]{5}\\-[0-9]{3}$', $String);
		}


		/**
		* Función responsable de validar el formato de un Povider ID.
		* @param string $providerId, valor a validar.
		* @return boolean, resultado de la validación. TRUE en caso de que el 
		* provider Id sea correcto.
		* @created 21/FEB/2013
		* @author Sebastián Lara <slara@grvertice.com>
		*/
		function validateProviderID($providerId) {
			return (is_string ($providerId) && @eregi ('^[0-9a-z]{4,5}\\-[0-9a-z]{7}$', $providerId));
		}


		/**
		* Función responsable de validar que una fecha cumpla con el formato
		* necesario para consultar en base de datos.
		* @param date $date, fecha a validar.
		* @return boolean, resultado de la validación. TRUE en caso de que la 
		* fecha tenga el formato correcto.
		* @created 31/JAN/2013
		* @author Sebastián Lara <slara@grvertice.com>
		*/
		function validateDate($date)
		{
			$result = FALSE;
			list ($yy, $mm, $dd) = explode ('-', $date);

		    if (is_numeric ($yy) && is_numeric ($mm) && is_numeric ($dd)) 
		    { 
		        $result = checkdate ($mm, $dd, $yy); 
		    } 

			return $result;
		}


		/**
		* Función responsable de validar que los parámetros que llegan satisfagan
		* los parámetros requeridos por un servicio.
		* @created 14/FEB/2013
		* @author Sebastián Lara <slara@grvertice.com>
		*/
		function validateRequestOrderParameters() {
			// llaves de los parámetros que deben de llegar
			$serviceIdParameterKey 	 = 'SERVICE_ID'; #
			$imeiParameterKey 	 	 = 'IMEI'; #
			$countryIdParameterKey 	 = 'COUNTRY_ID'; #
			$mobileIdParameterKey 	 = 'MOBILE_ID'; #
			$networkIdParameterKey 	 = 'NETWORK_ID'; #
			$mepParameterKey 		 = 'MEP'; #
			$providerIdParameterKey  = 'PROVIDER_ID'; #
			$userParameterKey 		 = 'USER'; #
			$serieParameterKey 		 = 'SERIE'; #
			$idBoxParameterKey 		 = 'ID_BOX'; #
			$descriptionParameterKey = 'DESCRIPTION';
			$nickParameterKey 		 = 'NICK';
			$passwordParameterKey 	 = 'PASSWORD';
			$rmParameterKey 		 = 'RM';

			// se capturan los valores de los parámetros
			$serviceId 	 = $this->parametersRequiredList[$serviceIdParameterKey]['VALUE'];
			$imei 		 = $this->parametersRequiredList[$imeiParameterKey]['VALUE'];
			$countryId 	 = $this->parametersRequiredList[$countryIdParameterKey]['VALUE'];
			$mobileId 	 = $this->parametersRequiredList[$mobileIdParameterKey]['VALUE'];
			$networkId	 = $this->parametersRequiredList[$networkIdParameterKey]['VALUE'];
			$mep 		 = $this->parametersRequiredList[$mepParameterKey]['VALUE'];
			$providerId  = $this->parametersRequiredList[$providerIdParameterKey]['VALUE'];
			$user 		 = $this->parametersRequiredList[$userParameterKey]['VALUE'];
			$serie 		 = $this->parametersRequiredList[$serieParameterKey]['VALUE'];
			$idBox 		 = $this->parametersRequiredList[$idBoxParameterKey]['VALUE'];
			$description = $this->parametersRequiredList[$descriptionParameterKey]['VALUE'];
			$nick 		 = $this->parametersRequiredList[$nickParameterKey]['VALUE'];
			$password    = $this->parametersRequiredList[$passwordParameterKey]['VALUE'];
			$rm 		 = $this->parametersRequiredList[$rmParameterKey]['VALUE'];

			$dataService = $this->server->getDataService($serviceId);
			
			// si un servicio es válido, es ilógico que el resultado
			// de la consulta sea NO_DATA_FOUND
			if ($dataService == self :: INTERNAL_ERROR) {
				$this->sendResponse(325);
			} elseif ($dataService == self :: NO_DATA_FOUND) {
				$this->sendResponse(328);
			} else {
				// se valida que los parámetros que necesita el servicio lleguen correctamente
				
				if ((strtolower ($dataService['type_service']) == 'imei') 
					|| ($dataService['ser_req_imei'] == 1)
					|| ($dataService['ser_req_imei'] == 2))  {
					// El imei es obligatorio 
					if (((strtolower ($dataService['type_service']) == 'imei') 
						|| $dataService['ser_req_imei'] == 2) && empty ($imei)) {
						// missing parameter
						$this->sendResponse(329, array ($imeiParameterKey));
					} else {
						/**
						 * En este caso se valida que el IMEI sea numérico y 
						 * tenga 15 dígitos, FALSE es para que el IMEI no lo 
						 * evalúe contra el algorito de validación estándar. 
						 */
						$isValidImei = $this->validateIMEI($imei, FALSE);

						if (!is_numeric($imei) || (int) $imei < 1) {
							// el valor debe ser numérico positivo
							$this->sendResponse(331, array ($imeiParameterKey, $imei));
						} 

						if ($isValidImei === FALSE) {
							// imei inválido
							$this->sendResponse(333, array ('IMEI', '15', 'IMEI', $imei));
						}
					}
				}


				if (($dataService['ser_req_models'] == 1) || ($dataService['ser_req_models'] == 2)) {
					if (($dataService['ser_req_models'] == 2) && empty ($mobileId)) {
						// missing parameter
						$this->sendResponse(329, array ($mobileIdParameterKey));
					} else {
						$isValidMobile = $this->server->validateMobile($mobileId, $serviceId);

						if (!is_numeric ($mobileId) || (int) $mobileId < 1) {
							// el valor debe ser numérico positivo
							$this->sendResponse(331, array ($mobileIdParameterKey, $mobileId));
						} 

						if ($isValidMobile === self :: INTERNAL_ERROR) {
							$this->sendResponse(325);
						} elseif ($isValidMobile === self :: NO_DATA_FOUND) {
							$this->sendResponse(328);
						} elseif ($isValidMobile === self :: NOT_SUPPORTED) {
							$this->sendResponse(335, array ($mobileId));
						} elseif ($isValidMobile === FALSE) {
							// parámetro desconocido
							$this->sendResponse(330, array ($mobileIdParameterKey, $mobileId));
						}
					}
				}

				if (($dataService['ser_req_country'] == 1) || ($dataService['ser_req_country'] == 2)) {
					
					if (($dataService['ser_req_country'] == 2) && empty ($countryId)) {
						// missing parameter
						$this->sendResponse(329, array ($countryIdParameterKey));
					} else {
						$isValidCountry = $this->server->validateCountry($countryId);

						if (!is_numeric($countryId) || (int) $countryId < 1 ) {
							$this->sendResponse(331, array ($countryIdParameterKey, $countryId));
						}

						if ($isValidCountry === self :: INTERNAL_ERROR) {
							$this->sendResponse(325);
						} elseif ($isValidCountry === self :: NO_DATA_FOUND) {
							$this->sendResponse(328);
						} elseif ($isValidCountry === FALSE) {
							// red no encontrada
							$this->sendResponse(330, array ($countryIdParameterKey, $countryId));
						}
					}
				}


				if (($dataService['ser_req_operator'] == 1) || ($dataService['ser_req_operator'] == 2)) {
					if (($dataService['ser_req_operator'] == 2) && empty ($networkId)) {
						// missing parameter
						$this->sendResponse(329, array ($networkIdParameterKey));
					} else {
						$isValidNetwork = $this->server->validateNetwork($networkId);

						if (!is_numeric($networkId) || (int) $networkId < 1 ) {
							$this->sendResponse(331, array ($networkIdParameterKey, $networkId));
						}

						if ($isValidNetwork === self :: INTERNAL_ERROR) {
							$this->sendResponse(325);
						} elseif ($isValidNetwork === self :: NO_DATA_FOUND) {
							$this->sendResponse(328);
						} elseif ($isValidNetwork === FALSE) {
							// red no encontrada
							$this->sendResponse(330, array ($networkIdParameterKey, $networkId));
						}
					}
				}

				if (($dataService['ser_req_MEP'] == 1) || ($dataService['ser_req_MEP'] == 2)) {
					if (($dataService['ser_req_MEP'] == 2) && empty ($mep)) {
						// missing parameter
						$this->sendResponse(329, array ($mepParameterKey));
					} else {
						$isValidMep = $this->validateMEP($mep);

						if ($isValidMep === FALSE) {
							// MEP inválido
							$this->sendResponse(334, array ($mepParameterKey, 'MEP-XXXXX-XXX', $mep));
						}
					}
				}


				if (($dataService['ser_req_id'] == 1) || ($dataService['ser_req_id'] == 2)) {
					if (($dataService['ser_req_id'] == 2) && empty ($providerId)) {
						// missing parameter
						$this->sendResponse(329, array ($providerIdParameterKey));
					} else {
						$isValidProviderId = $this->validateProviderID($providerId);

						if ($isValidProviderId === FALSE) {
							// MEP inválido
							$this->sendResponse(334, array ($providerIdParameterKey, 'XXXXX-XXXXXXX | XXXX-XXXXXXX', $providerId));
						}
					}
				}

				if (($dataService['ser_req_user'] == 1) || ($dataService['ser_req_user'] == 2)) {
					if (($dataService['ser_req_user'] == 2) && empty ($user)) {
						// missing parameter
						$this->sendResponse(329, array ($userParameterKey));
					}
				}

				if (($dataService['ser_req_serie'] == 1) || ($dataService['ser_req_serie'] == 2)) {
					if (($dataService['ser_req_serie'] == 2) && empty ($serie)) {
						// missing parameter
						$this->sendResponse(329, array ($serieParameterKey));
					}
				}

				if (($dataService['ser_req_idbox'] == 1) || ($dataService['ser_req_idbox'] == 2)) {
					if (($dataService['ser_req_idbox'] == 2) && empty ($idBox)) {
						// missing parameter
						$this->sendResponse(329, array ($idBoxParameterKey));
					}
				}

				if (($dataService['ser_req_nick'] == 1) || ($dataService['ser_req_nick'] == 2)) {
					if (($dataService['ser_req_nick'] == 2) && empty ($nick)) {
						// missing parameter
						$this->sendResponse(329, array ($nickParameterKey));
					}
				}

				if (($dataService['ser_req_pass'] == 1) || ($dataService['ser_req_pass'] == 2)) {
					if (($dataService['ser_req_pass'] == 2) && empty ($password)) {
						// missing parameter
						$this->sendResponse(329, array ($passwordParameterKey));
					}
				}

				if (($dataService['ser_req_rm'] == 1) || ($dataService['ser_req_rm'] == 2)) {
					if (($dataService['ser_req_rm'] == 2) && empty ($rm)) {
						// missing parameter
						$this->sendResponse(329, array ($rmParameterKey));
					}
				}

				// si se pasan todas las validaciones para el servicio, se guardan los
				// datos del servicio proseguir a realizar el unlock.
				$this->dataUnlockService = $dataService;
			}
		}

		/**
		* Función responsable de validar que los créditos que posee el cliente
		* sean suficientes para realizar la liberación. En caso de presentarse
		* una inconsistencia devolver el mensaje respectivo.
		* @created 21/FEB/2013
		* @author Sebastián Lara <slara@grvertice.com>
		*/
		function validateCredits() {
			// validamos que los créditos del cliente sean suficientes para 
			// enviar el servicio.
			if ($this->dataUnlockService['ser_price_client'] > $this->server->getCreditsClient()) {
				// créditos insuficientes
				$this->sendResponse(336, array ($this->server->getCreditsClient(), $this->dataUnlockService['ser_price_client']));
			}

		}

		/**
		* Función responsable da hacer la validación según la forma de liberar
		* del servicio: Api | Manual | Lista | Algoritmo.
		* @created 26/FEB/2013
		* @author Sebastián Lara <slara@grvertice.com>
		*/
		function validateParametersUnlockWay() {
			$this->sendResponse($this->dataUnlockService['API_provider_code']);
		}

	}
	
	// Initialize Library
	
	$api = new API();
	$api->processApi();